package nachos.network;

import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;
import nachos.vm.*;
import nachos.network.*;

/**
 * A kernel with network support.
 */
public class NetKernel extends UserKernel {
    /**
     * Allocate a new networking kernel.
     */
   public NetKernel() {
      super();

    }

    /**
     * Initialize this kernel.
     */
    public void initialize(String[] args) {
	super.initialize(args);

   if( postOffice == null ) {
      postOffice = new PostOffice();
   }
    }

    /**
     * Test the network. Create a server thread that listens for pings on port
     * 1 and sends replies. Then ping one or two hosts. Note that this test
     * assumes that the network is reliable (i.e. that the network's
     * reliability is 1.0).
     */
    public void selfTest() {
       super.selfTest();

       //(KYLE) replacing with connection impl
       //pingSelfTest();

       if(Lib.test('s'))
        connectionSelfTest();

    }

    private void connectionSelfTest() {
    	
       int local = Machine.networkLink().getLinkAddress();

       System.out.println("[Group 10 Connection test]");

       //setup a mutual connection - assumed to be between links 1 and 0

      if(local == 0)
      {
          System.out.println("Waiting on accept...");
          Connection c = null;
          while(c == null)
          {
            c = postOffice.accept(2);
            KThread.yield();
          }

          System.out.println("Connection Accepted!");
          c.close();
      }
      else
      {
           System.out.println("Waiting for connect...");
           Connection c = null;
           c = postOffice.connect(0, 2);

           if(c != null && c.state != Connection.DEADLOCK && c.state != Connection.PROTOCOL_ERROR)
           {
               c.connectSemaphore.P();
               System.out.println("Connect!! -  state: " + c.state);
               c.close();
           }
           else
           {
              System.out.println("Problem connecting!");
           }
      }

    }

    /**
     * Start running user programs.
     */
    public void run() {
	super.run();
    }
    
    /**
     * Terminate this kernel. Never returns.
     */
    public void terminate() {
	super.terminate();
    }

    public static PostOffice getPostOffice() {
       return postOffice;
    }

    private static PostOffice postOffice;

    // dummy variables to make javac smarter
    private static NetProcess dummy1 = null;


//-----------------------------------------------------------------
//-----------------------------------------------------------------

//(KYLE) Don't get confused by all the following stuff, guys,
//It's just the ping test that serves as an example...

//To use it, just comment out connectionSelfTest() and uncomment pingSelfTest()
//in the selfTest() function.

    private void pingSelfTest() {

       KThread serverThread = new KThread(new Runnable() {
          public void run() { pingServer(); }
       });

       serverThread.fork();

       System.out.println("[Ping Test] Press any key to start the network test...");
       console.readByte(true);

       int local = Machine.networkLink().getLinkAddress();

       // ping this machine first
       ping(local);

       // if we're 0 or 1, ping the opposite
       if (local <= 1) {
          ping(1-local);
       }
    }

    private void ping(int dstLink) {
	int srcLink = Machine.networkLink().getLinkAddress();
	
	System.out.println("PING " + dstLink + " from " + srcLink);

	long startTime = Connection.now();
	
	MailMessage ping;

    Connection c = new Connection(dstLink, 2, Machine.networkLink().getLinkAddress(), -1, false);

	ping = c.makeData(new byte[0]);

	postOffice.send(ping);

	MailMessage ack = postOffice.receive(0);

	long endTime = Connection.now();

	System.out.println("time=" + (endTime-startTime) + " ticks");	
    }

    private void pingServer() {
	while (true) {
	    MailMessage ping = postOffice.receive(1);

	    MailMessage ack;

	    try {
      ack = new MailMessage(ping.srcLink, ping.srcPort,
            ping.dstLink, ping.dstPort, 1, new byte[0]);
      ack.setAck();
	    }
	    catch (MalformedPacketException e) {
		// should never happen...
		continue;
	    }

	    postOffice.send(ack);
	}	
    }
    
}
